﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MarioArena.Characters;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace MarioArena.World
{
    public class Opponent
    {
        private const int NametagMarginBottom = 18;

        public int Id { get; set; }
        public int Ping { get; set; }
        public string Name { get; set; }
        public bool IsReadyInLobby { get; set; }

        /// <summary>
        /// The character of this opponent. This property is set by the TCP connection when the game is starting.
        /// </summary>
        public Character Character { get; set; }
        public bool LeftKeyPressed { get; set; }
        public bool RightKeyPressed { get; set; }
        public bool JumpKeyPressed { get; set; }

        private int mPressedKey = 0;
        private bool mDecelerateRight = false;
        private bool mDecelerateLeft = false;

        /// <summary>
        /// The elapsed time between the two latest updates. This is used to create machine-independance. 
        /// The player will move just as fast on a very slow PC as on a fast pc.
        /// </summary>
        private float mElapsedTime = 0.016667f;

        private Vector2 mNameTagSize;

        /// <summary>
        /// Constructs a new opponent.
        /// </summary>
        /// <param name="id">The ID of the opponent.</param>
        /// <param name="name">The name of the opponent.</param>
        public Opponent(int id, string name)
        {
            this.Id = id;
            this.Name = name;
        }

        /// <summary>
        /// Updates the opponent.
        /// </summary>
        public void Update(GameTime gameTime)
        {
            // Elapsed time between this and the last update in seconds (< 0)
            this.mElapsedTime = (float)(gameTime.ElapsedGameTime.TotalSeconds);

            HandleInput();
            this.Character.Update(gameTime);

            this.Character.CameraX += (int)(Convert.ToInt32((this.Character.SpeedX * Character.Direction) * mElapsedTime) * 0.5f);
            this.Character.CameraY += (int)(Convert.ToInt32(this.Character.SpeedY * mElapsedTime));

            this.Character.Bounds = new Rectangle(Character.CameraX - (this.Character.Width / 2), Character.CameraY - this.Character.MarginTop, this.Character.Width, this.Character.Height);
        }

        /// <summary>
        /// Draws the opponent.
        /// </summary>
        public void Draw(SpriteBatch spriteBatch)
        {
            this.Character.Draw(spriteBatch);

            if (!this.Character.IsDead) //only draw if alive
            {
                this.mNameTagSize = GameManager.Instance.NametagFont.MeasureString(this.Name);

                // Draw the nametag above the character, centering it horizontally
                Vector2 textPosition = new Vector2(this.Character.CameraX - (this.mNameTagSize.X / 2) + GameManager.Instance.HalfScreenWidth - Camera.X,
                    this.Character.CameraY - this.Character.Height - NametagMarginBottom + GameManager.Instance.HalfScreenHeight - Camera.Y);

                // Draw text shadow first
                spriteBatch.DrawString(GameManager.Instance.NametagFont, this.Name, textPosition + new Vector2(1, 1), Color.Black);

                // Draw the text
                spriteBatch.DrawString(GameManager.Instance.NametagFont, this.Name, textPosition, Color.Red);
            }
        }

        /// <summary>
        /// Override used to display this object in the PlayerList in the lobby.
        /// </summary>
        /// <returns>The name of the opponent.</returns>
        public override string ToString()
        {
            return this.Name;
        }

        /// <summary>
        /// Handles the input reponse for the opponent.
        /// </summary>
        private void HandleInput()
        {
            // Keys input
            if (this.LeftKeyPressed || this.RightKeyPressed)
            {
                if (this.LeftKeyPressed)
                {
                    mDecelerateLeft = false; //Player is not decelerating in this direction anymore

                    if (this.Character.Direction == 0) this.Character.Direction = -1; //first press
                    if (mPressedKey == 0) mPressedKey = -1;
                    if (mPressedKey == 1) //If previous key was D
                        mDecelerateRight = true; //Start decelerating in the right direction
                    mPressedKey = -1;
                }
                else if (this.RightKeyPressed)
                {
                    mDecelerateRight = false; //Player is not decelerating in this direction anymore

                    if (this.Character.Direction == 0) this.Character.Direction = 1; //first press
                    if (mPressedKey == 0) mPressedKey = 1;
                    if (mPressedKey == -1) //If previous key was A
                        mDecelerateLeft = true; //Start decelerating in the left direction
                    mPressedKey = 1;
                }

                if (mDecelerateRight) //If busy decelerating in the right direction
                {
                    this.Character.SpeedX -= (this.Character.TurningDecelerationX * this.Character.AccelerationScale) * this.mElapsedTime; //Decelerate
                    if (this.Character.SpeedX < (this.Character.TurningDecelerationX * this.Character.AccelerationScale) * this.mElapsedTime) //When (nearly) standing still...
                    {
                        mDecelerateRight = false; //... stop deceleration
                        this.Character.Direction = -1; //Set new direction for acceleration
                    }
                }
                else if (mDecelerateLeft) //If busy decelerating in the left direction
                {
                    this.Character.SpeedX -= (this.Character.TurningDecelerationX * this.Character.AccelerationScale) * this.mElapsedTime; //Descelerate
                    if (this.Character.SpeedX <= (this.Character.TurningDecelerationX * this.Character.AccelerationScale) * this.mElapsedTime) //When (nearly) standing still...
                    {
                        mDecelerateLeft = false; //... stop deceleration
                        this.Character.Direction = 1; //Set new direction for acceleration
                    }
                }
                else this.Character.SpeedX += (this.Character.AccelerationX * this.Character.AccelerationScale) * this.mElapsedTime;// Accelerate horizontally

                if (this.Character.SpeedX > this.Character.MaxSpeedX)
                    this.Character.SpeedX = this.Character.MaxSpeedX;
            }

            if (this.JumpKeyPressed)
            {
                // Only jump if not already in air
                if (this.Character.PlayerState != PlayerState.Falling && this.Character.PlayerState != PlayerState.Jumping)
                {
                    // Check if the tile index isn't out of bounds to prevent index out of range exceptions
                    if (this.Character.IsTileIndexInBounds())
                    {
                        // Check if the player is currently standing on tiles. Don't jump otherwise
                        Tile bottomTile = TileEngine.Instance.Tiles[this.Character.TileIndex.X, this.Character.TileIndex.Y + 1];
                        Tile bottomLeftTile = TileEngine.Instance.Tiles[this.Character.TileIndex.X - 1, this.Character.TileIndex.Y + 1];
                        Tile bottomRightTile = TileEngine.Instance.Tiles[this.Character.TileIndex.X + 1, this.Character.TileIndex.Y + 1];
                        if (bottomTile.Texture != null || bottomLeftTile.Texture != null || bottomRightTile.Texture != null)
                        {
                            this.Character.PlayerState = PlayerState.Jumping;
                            this.Character.SpeedY = -this.Character.MaxSpeedY;
                        }
                    }
                }
            }

            // NOT in air
            if (!this.LeftKeyPressed && !this.RightKeyPressed && // No horizontal input and
                this.Character.PlayerState != PlayerState.Falling && this.Character.PlayerState != PlayerState.Jumping) // Player is on the ground
            {
                // Decelerate horizontally
                this.Character.SpeedX -= this.Character.DecelerationX * this.mElapsedTime;
                if (this.Character.SpeedX < 0)
                    this.Character.SpeedX = 0;
            }
            else // In air
            {
                // Accelerate vertically
                this.Character.SpeedY += this.Character.AccelerationY * this.mElapsedTime;
                if (this.Character.SpeedY > this.Character.MaxSpeedY)
                    this.Character.SpeedY = this.Character.MaxSpeedY;
                if (this.Character.PlayerState == PlayerState.Falling)
                {
                    if (this.Character.SpeedY < 0)
                        this.Character.PlayerState = PlayerState.Jumping;
                }
                else if (this.Character.PlayerState == PlayerState.Jumping)
                {
                    if (this.Character.SpeedY > 0)
                        this.Character.PlayerState = PlayerState.Falling;
                }
            }
        }
    }
}
